/**HEADER********************************************************************
* 
* Copyright (c) 2008 Freescale Semiconductor;
* All Rights Reserved
*
*************************************************************************** 
*
* THIS SOFTWARE IS PROVIDED BY FREESCALE "AS IS" AND ANY EXPRESSED OR 
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  
* IN NO EVENT SHALL FREESCALE OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 
* THE POSSIBILITY OF SUCH DAMAGE.
*
**************************************************************************
*
* $FileName: rtc_mpc83xx.c$
* $Version : 3.8.1.0$
* $Date    : May-12-2011$
*
* Comments
*    This simple RTC driver uses a 1 second interrupt and manages time and all 
*    other features in software
*
*
*END************************************************************************/

#include <mqx.h>
#include <bsp.h>
#include <rtc.h>


static RTC_TIME_STRUCT real_time = {0};
static RTC_TIME_STRUCT alarm_time = {0};
static uint_32 stopwatch = 0;
static uint_32 rtc_event_enable = 0;
static uint_32 rtc_event_flag = 0;
void (_CODE_PTR_ user_isr)(pointer) = NULL;

/*FUNCTION****************************************************************
* 
* Function Name    : _bsp_get_rtc_base_address
* Returned Value   : pointer to base of RTC registers
* Comments         :
*    This function returns base address of RTC related register space.
*
*END*********************************************************************/
pointer _bsp_get_rtc_base_address 
(
    void
)
{
    uint_32 immr   = (uint_32)BSP_IMMR_BASE;
    
    return (pointer)((MPC83xx_RTC_STRUCT_PTR)(immr + MPC83xx_RTC_BASE));
}

/*FUNCTION****************************************************************
* 
* Function Name    : _bsp_get_rtc_vector
* Returned Value   : MQX vector number for specified interrupt
* Comments         :
*    This function returns index into MQX interrupt vector table for
*    RTC interrupt.
*
*END*********************************************************************/
PSP_INTERRUPT_TABLE_INDEX _bsp_get_rtc_vector 
(
    void
)
{
    return MPC83xx_RTC_VECTOR;
}

/*FUNCTION****************************************************************
* 
* Function Name    : _rtc_init
* Returned Value   : MQX error code
* Comments         :
*    This function (re)initializes/clears/enables RTC module.
*    If cleared, alarm happens after RTC overflow, stopwatch happens next minute.
*    Reset disables and clears all interrupts and stopwatch (even if cleared).
*
*END*********************************************************************/
uint_32 _rtc_init 
(
    /* [IN] flags identifying init operations */
    uint_32 flags
)
{
    MPC83xx_RTC_STRUCT_PTR rtc_ptr;
    
    uint_32 result = MQX_OK;
    uint_32 immr   = (uint_32)BSP_IMMR_BASE;

    #if MQX_CPU == PSP_CPU_MPC8308
       /* 8308 RTC requires a reset */   
       uint_32 delay, tmp; 
       MPC83xx_SYSCON_STRUCT_PTR syscon_ptr;
       syscon_ptr = (MPC83xx_SYSCON_STRUCT_PTR)(immr + MPC83xx_SYSCON_BASE);
       syscon_ptr->RTCCR = MPC8308_SYSCON_RTCCR_RESET_RTC;
       _time_delay_ticks(10);
       syscon_ptr->RTCCR = 0;
       _time_delay_ticks(10);
    #endif
        
    rtc_ptr    = (MPC83xx_RTC_STRUCT_PTR)(immr + MPC83xx_RTC_BASE);
    
    /* Disable Everything */
    rtc_ptr->RTCNR = 0;
    
    /* Clear any pending intrrupts */
    rtc_ptr->RTEVR |= rtc_ptr->RTEVR;
    
    // clear time, alarm and stopwatch
    if (flags & RTC_INIT_FLAG_CLEAR) {      
      real_time.seconds = 0;
      real_time.minutes = 0;
      real_time.hours = 0;
      real_time.days = 0;
      alarm_time.seconds = 0;
      alarm_time.minutes = 0;
      alarm_time.hours = 0;
      alarm_time.days = 0;
      stopwatch = 0;
    }

    // reset current user interrupt state and callback
    if (flags & RTC_INIT_FLAG_RESET) {
      rtc_event_flag = 0;
      rtc_event_enable = 0;
      user_isr = NULL;
      stopwatch = (uint_32)-1;
    }

    // install HW interrupt and start the RTC for 1 second interrupts
    if (flags & RTC_INIT_FLAG_ENABLE) {
      if (NULL == _int_install_isr (MPC83xx_RTC_VECTOR, (void (_CODE_PTR_))_rtc_isr, (pointer)rtc_ptr)) result = _task_get_error ();
      if (MQX_OK == result) {
         _mpc83xx_enable_interrupt(MPC83xx_RTC_VECTOR);
         rtc_ptr->RTCNR |= MPC83xx_RTC_RTCNR_CLK_ENABLE | MPC83xx_RTC_RTCNR_SEC_INT_ENABLE;
      }
    }

    return result;
}

/*FUNCTION****************************************************************
* 
* Function Name    : _rtc_isr
* Returned Value   : none
* Comments         :
*    This is ISR for RTC module, triggered each second.
*    Does not respond to alarm interrupt.
*
*END*********************************************************************/
void _rtc_isr 
(
    /* [IN] rtc module pointer passed to interrupt */
    pointer ptr
)
{
    MPC83xx_RTC_STRUCT_PTR rtc_ptr = (MPC83xx_RTC_STRUCT_PTR)ptr;
    
    if (rtc_ptr->RTEVR && MPC83xx_RTC_RTEVR_SEC_INT) 
    {
       rtc_ptr->RTEVR &= ~MPC83xx_RTC_RTEVR_SEC_INT;   // clear interrupt bit

       rtc_event_flag |= RTC_EVENT_1HZ;               // increment second count
       real_time.seconds++;
       
       if (real_time.seconds > 59)                    // minute boundary
       {
          real_time.seconds = 0;
          rtc_event_flag |= RTC_EVENT_MIN;
          if (stopwatch != (uint_32)-1)               // stopwatch decrementer
          {
             stopwatch--;
             if (stopwatch == (uint_32)-1) 
             {
               rtc_event_flag |= RTC_EVENT_SW;
             }
          }          
          real_time.minutes++;
          if (real_time.minutes > 59)                 // hour boundary
          {
             real_time.minutes = 0;
             rtc_event_flag |= RTC_EVENT_HR;
             real_time.hours++;
             if (real_time.hours > 23)                // days boundary
             {
                real_time.hours = 0;
                rtc_event_flag |= RTC_EVENT_DAY;
                real_time.days++;
             }    
          }    
       }
    
       // alarm checking
       if ((real_time.seconds == alarm_time.seconds) && (real_time.minutes == alarm_time.minutes) && (real_time.hours == alarm_time.hours) && (real_time.days == alarm_time.days)) {
          rtc_event_flag |= RTC_EVENT_ALM;
       }
       // user callback triggering
       if ((NULL != user_isr) && (rtc_event_flag & rtc_event_enable)) user_isr ((pointer)rtc_ptr);
    }
}

/*FUNCTION****************************************************************
* 
* Function Name    : _rtc_int_install
* Returned Value   : MQX error code
* Comments         :
*    This function installs a user callback for alarm and stopwatch events.
*    Can be used for other events/features too
*
*END*********************************************************************/
uint_32 _rtc_int_install 
(
    /* [IN] pointer to user ISR code */
    pointer isr
)
{
    user_isr = isr;
    return MQX_OK;
}

/*FUNCTION****************************************************************
* 
* Function Name    : _rtc_int_enable
* Returned Value   : bitmask of new int enable state
* Comments         :
*    This function enables/disables RTC interrupts according to specified bitmask.
*
*END*********************************************************************/
uint_32 _rtc_int_enable 
(
    /* [IN] whether to enable or disable interrupts */
    boolean enable, 
    /* [IN] bitmask of affected interrupts */
    uint_32 bitmask
)
{
    if (enable){
      rtc_event_enable |= bitmask;
    } else {
      rtc_event_enable &= (~ bitmask);
    }
    return rtc_event_enable;
}
                              
/*FUNCTION****************************************************************
* 
* Function Name    : _rtc_clear_requests
* Returned Value   : none
* Comments         :
*    This function clears RTC interrupt requests according to given bitmask.
*
*END*********************************************************************/
void _rtc_clear_requests 
(
    /* [IN] bitmask of affected interrupts */
    uint_32 bitmask
)
{
    rtc_event_flag &= (~ bitmask);
}

/*FUNCTION****************************************************************
* 
* Function Name    : _rtc_get_status
* Returned Value   : bitmask of actual RTC requests + RTC enabled bit
* Comments         :
*    This function returns bitmask of pending RTC requests along
*    with RTC enabled bit.
*
*END*********************************************************************/
uint_32 _rtc_get_status 
(
    void
)
{
    MPC83xx_RTC_STRUCT_PTR rtc_ptr;
    uint_32 immr   = (uint_32)BSP_IMMR_BASE;
    uint_32 result = rtc_event_flag;

    rtc_ptr    = (MPC83xx_RTC_STRUCT_PTR)(immr + MPC83xx_RTC_BASE); 
    if (rtc_ptr->RTCNR & MPC83xx_RTC_RTCNR_CLK_ENABLE) {
       result |= MPC83xx_RTC_RTCNR_CLK_ENABLE;
    }
    return result;
}

/*FUNCTION****************************************************************
* 
* Function Name    : _rtc_set_time
* Returned Value   : none
* Comments         :
*    This function sets the RTC time according to given time struct.
*
*END*********************************************************************/
void _rtc_set_time 
(
    /* [IN] given time to be set as RTC time */
    RTC_TIME_STRUCT_PTR time
)
{
    MPC83xx_RTC_STRUCT_PTR rtc_ptr;
    uint_32 immr   = (uint_32)BSP_IMMR_BASE;
    uint_32 tmp;

    rtc_ptr = (MPC83xx_RTC_STRUCT_PTR)(immr + MPC83xx_RTC_BASE); 
    tmp = rtc_ptr->RTCNR & MPC83xx_RTC_RTCNR_CLK_ENABLE;   
    rtc_ptr->RTCNR &= (~ MPC83xx_RTC_RTCNR_CLK_ENABLE);                 // disable RTC
    real_time = *time;                                                  // set new time
    if (real_time.seconds > 59) real_time.seconds = 59;                 // correct time
    if (real_time.minutes > 59) real_time.minutes = 59;
    if (real_time.hours > 23) real_time.hours = 23;
    rtc_ptr->RTCNR |= MPC83xx_RTC_RTCNR_SEC_INT_ENABLE;                     // clear RTC 1 second int request
    rtc_ptr->RTCNR |= tmp;                                                  // restore RTC enable state
}

/*FUNCTION****************************************************************
* 
* Function Name    : _rtc_get_time
* Returned Value   : none
* Comments         :
*    This function gets the RTC time and stores it in given time struct.
*
*END*********************************************************************/
void _rtc_get_time 
(
    /* [OUT] this parameter gets actual RTC time */
    RTC_TIME_STRUCT_PTR time
)
{
    RTC_TIME_STRUCT tmp;  
    time->hours = 0xFF;
    do   // loop until same time is read twice - keeping time integrity
    {
      tmp = *time;
      *time = real_time;
    } while ((tmp.seconds != time->seconds) || (tmp.minutes != time->minutes) || (tmp.hours != time->hours) || (tmp.days != time->days));
}

/*FUNCTION****************************************************************
* 
* Function Name    : _rtc_set_alarm
* Returned Value   : none
* Comments         :
*    This function sets RTC alarm time according to given time struct.
*    Alarm happens immediately after match.
*
*END*********************************************************************/
void _rtc_set_alarm 
(
    /* [IN] time to be set as RTC alarm time */
    RTC_TIME_STRUCT_PTR time
)
{
    uint_32 tmp = rtc_event_enable & RTC_EVENT_ALM;         // save alarm enable state
    rtc_event_enable &= (~ RTC_EVENT_ALM);                  // clear alarm enable
    alarm_time = *time;                                     // set new alarm time
    if (alarm_time.seconds > 59) alarm_time.seconds = 59;   // correct alarm time
    if (alarm_time.minutes > 59) alarm_time.minutes = 59;
    if (alarm_time.hours > 23) alarm_time.hours = 23;
    rtc_event_flag &= (~ RTC_EVENT_ALM);                    // clear alarm flag
    rtc_event_enable |= tmp;                                // restore alarm enable state
}

/*FUNCTION****************************************************************
* 
* Function Name    : _rtc_get_alarm
* Returned Value   : none
* Comments         :
*    This function gets the RTC alarm time and stores it in given time struct.
*
*END*********************************************************************/
void _rtc_get_alarm 
(
    /* [OUT] this parameter gets the RTC alarm time */
    RTC_TIME_STRUCT_PTR time
)
{
    *time = alarm_time;
}

/*FUNCTION****************************************************************
* 
* Function Name    : _rtc_set_stopwatch
* Returned Value   : none
* Comments         :
*    This function sets the RTC stopwatch decrementer value (minutes).
*    Stopwatch decrements each new RTC minute and stops (disables) at -1.
*
*END*********************************************************************/
void _rtc_set_stopwatch 
(
    /* [IN] number of minutes to countdown */
    uint_32 minutes
)
{
    uint_32 tmp = rtc_event_enable & RTC_EVENT_SW;             // save stopwatch enable state
    rtc_event_enable &= (~ RTC_EVENT_SW);                      // clear stopwatch enable
    stopwatch = RTC_STPWCH_CNT(minutes);                       // set new stopwatch
    rtc_event_flag &= (~ RTC_EVENT_SW);                        // clear stopwatch flag
    rtc_event_enable |= tmp;                                   // restore stopwatch enable state
}

/*FUNCTION****************************************************************
* 
* Function Name    : _rtc_get_stopwatch
* Returned Value   : actual RTC minute stopwatch counter value
* Comments         :
*    This function returns the actual value of RTC stopwatch decrementer.
*
*END*********************************************************************/
uint_32 _rtc_get_stopwatch 
(
    void
)
{
    return stopwatch;
}
/* EOF */

